From 2d6539154b91d305606478d03dc4505a7ee40dd7 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 8 Sep 2014 16:01:38 -0700 Subject: [PATCH] Implement Sha256 bindings These are used to verify the downloads of packages from the registry. --- src/cargo/lib.rs | 3 +- src/cargo/sources/registry.rs | 40 +++++++-- src/cargo/util/mod.rs | 4 +- src/cargo/util/sha256.rs | 162 ++++++++++++++++++++++++++++++++++ 4 files changed, 202 insertions(+), 7 deletions(-) create mode 100644 src/cargo/util/sha256.rs diff --git a/src/cargo/lib.rs b/src/cargo/lib.rs index 8ff267fcb..99a15bb0b 100644 --- a/src/cargo/lib.rs +++ b/src/cargo/lib.rs @@ -5,7 +5,7 @@ #![feature(default_type_params)] #![deny(warnings)] -extern crate glob; +extern crate libc; extern crate regex; extern crate serialize; extern crate term; @@ -17,6 +17,7 @@ extern crate curl; extern crate docopt; extern crate flate2; extern crate git2; +extern crate glob; extern crate semver; extern crate tar; extern crate toml; diff --git a/src/cargo/sources/registry.rs b/src/cargo/sources/registry.rs index edd096715..9579484ff 100644 --- a/src/cargo/sources/registry.rs +++ b/src/cargo/sources/registry.rs @@ -1,10 +1,13 @@ #![allow(unused)] use std::io::{mod, fs, File, MemReader}; +use std::collections::HashMap; + use curl::http; use git2; use semver::Version; use flate2::reader::GzDecoder; use serialize::json; +use serialize::hex::ToHex; use tar::Archive; use url::Url; @@ -12,7 +15,7 @@ use core::{Source, SourceId, PackageId, Package, Summary, Registry}; use core::Dependency; use sources::PathSource; use util::{CargoResult, Config, internal, ChainError, ToUrl, human}; -use util::{hex, Require}; +use util::{hex, Require, Sha256}; use ops; static CENTRAL: &'static str = "https://example.com"; @@ -25,6 +28,7 @@ pub struct RegistrySource<'a, 'b:'a> { config: &'a mut Config<'b>, handle: http::Handle, sources: Vec, + hashes: HashMap<(String, String), String>, // (name, vers) => cksum } #[deriving(Decodable)] @@ -32,6 +36,14 @@ struct RegistryConfig { dl_url: String, } +#[deriving(Decodable)] +struct RegistryPackage { + name: String, + vers: String, + deps: Vec, + cksum: String, +} + impl<'a, 'b> RegistrySource<'a, 'b> { pub fn new(source_id: &SourceId, config: &'a mut Config<'b>) -> RegistrySource<'a, 'b> { @@ -46,6 +58,7 @@ impl<'a, 'b> RegistrySource<'a, 'b> { source_id: source_id.clone(), handle: http::Handle::new(), sources: Vec::new(), + hashes: HashMap::new(), } } @@ -116,6 +129,23 @@ impl<'a, 'b> RegistrySource<'a, 'b> { return Err(internal(format!("Failed to get 200 reponse from {}\n{}", url, resp))) } + + // Verify what we just downloaded + let expected = self.hashes.find(&(pkg.get_name().to_string(), + pkg.get_version().to_string())); + let expected = try!(expected.require(|| { + internal(format!("no hash listed for {}", pkg)) + })); + let actual = { + let mut state = Sha256::new(); + state.update(resp.get_body()); + state.final() + }; + if actual.as_slice().to_hex() != *expected { + return Err(human(format!("Failed to verify the checksum of `{}`", + pkg))) + } + try!(File::create(&dst).write(resp.get_body())); Ok(dst) } @@ -150,8 +180,8 @@ impl<'a, 'b> RegistrySource<'a, 'b> { impl<'a, 'b> Registry for RegistrySource<'a, 'b> { fn query(&mut self, dep: &Dependency) -> CargoResult> { - let path = &self.checkout_path; let mut chars = dep.get_name().chars(); + let path = self.checkout_path.clone(); let path = path.join(format!("{}{}", chars.next().unwrap_or('X'), chars.next().unwrap_or('X'))); let path = path.join(format!("{}{}", chars.next().unwrap_or('X'), @@ -165,10 +195,8 @@ impl<'a, 'b> Registry for RegistrySource<'a, 'b> { let ret: CargoResult>; ret = contents.as_slice().lines().filter(|l| l.trim().len() > 0) .map(|l| { - #[deriving(Decodable)] - struct Package { name: String, vers: String, deps: Vec } - let pkg = try!(json::decode::(l)); + let pkg = try!(json::decode::(l)); let pkgid = try!(PackageId::new(pkg.name.as_slice(), pkg.vers.as_slice(), &self.source_id)); @@ -181,6 +209,8 @@ impl<'a, 'b> Registry for RegistrySource<'a, 'b> { Dependency::parse(name, Some(vers), &self.source_id) }).collect(); let deps = try!(deps); + let RegistryPackage { name, vers, cksum, .. } = pkg; + self.hashes.insert((name, vers), cksum); Ok(Summary::new(&pkgid, deps.as_slice())) }).collect(); let mut summaries = try!(ret.chain_error(|| { diff --git a/src/cargo/util/mod.rs b/src/cargo/util/mod.rs index 33bc04269..38614d307 100644 --- a/src/cargo/util/mod.rs +++ b/src/cargo/util/mod.rs @@ -12,6 +12,7 @@ pub use self::dependency_queue::Dependency; pub use self::graph::Graph; pub use self::to_url::ToUrl; pub use self::vcs::{GitRepo, HgRepo}; +pub use self::sha256::Sha256; pub mod graph; pub mod process_builder; @@ -26,4 +27,5 @@ pub mod profile; mod pool; mod dependency_queue; mod to_url; -pub mod vcs; +mod vcs; +mod sha256; diff --git a/src/cargo/util/sha256.rs b/src/cargo/util/sha256.rs new file mode 100644 index 000000000..030bbd361 --- /dev/null +++ b/src/cargo/util/sha256.rs @@ -0,0 +1,162 @@ +pub use self::imp::Sha256; + +// Someone upstream will link to OpenSSL, so we don't need to explicitly +// link to it ourselves. Hence we pick up Sha256 digests from OpenSSL +#[cfg(not(windows))] +mod imp { + use libc; + + #[repr(C)] struct EVP_MD_CTX; + #[repr(C)] struct EVP_MD; + #[repr(C)] struct ENGINE; + + extern { + fn EVP_DigestInit_ex(ctx: *mut EVP_MD_CTX, + kind: *const EVP_MD, + imp: *mut ENGINE) -> libc::c_int; + fn EVP_DigestUpdate(ctx: *mut EVP_MD_CTX, + d: *const libc::c_void, + cnt: libc::size_t) -> libc::c_int; + fn EVP_DigestFinal_ex(ctx: *mut EVP_MD_CTX, md: *mut libc::c_uchar, + s: *mut libc::c_uint) -> libc::c_int; + fn EVP_MD_CTX_create() -> *mut EVP_MD_CTX; + fn EVP_MD_CTX_destroy(ctx: *mut EVP_MD_CTX); + fn EVP_sha256() -> *const EVP_MD; + } + + pub struct Sha256 { ctx: *mut EVP_MD_CTX } + + impl Sha256 { + pub fn new() -> Sha256 { + unsafe { + let ctx = EVP_MD_CTX_create(); + assert!(!ctx.is_null()); + let ret = Sha256 { ctx: ctx }; + let n = EVP_DigestInit_ex(ret.ctx, EVP_sha256(), 0 as *mut _); + assert_eq!(n, 1); + return ret; + } + } + + pub fn update(&mut self, bytes: &[u8]) { + unsafe { + let n = EVP_DigestUpdate(self.ctx, bytes.as_ptr() as *const _, + bytes.len() as libc::size_t); + assert_eq!(n, 1); + } + } + + pub fn final(&mut self) -> [u8, ..32] { + unsafe { + let mut ret = [0u8, ..32]; + let mut out = 0; + let n = EVP_DigestFinal_ex(self.ctx, ret.as_mut_ptr(), &mut out); + assert_eq!(n, 1); + assert_eq!(out, 32); + return ret; + } + } + } + + impl Drop for Sha256 { + fn drop(&mut self) { + unsafe { EVP_MD_CTX_destroy(self.ctx) } + } + } +} + +// Leverage the crypto APIs that windows has built in. +#[cfg(windows)] +mod imp { + use std::os; + + use libc; + use libc::{DWORD, BYTE, LPCSTR, BOOL}; + use libc::types::os::arch::extra::{LONG_PTR}; + + type HCRYPTPROV = LONG_PTR; + type HCRYPTHASH = LONG_PTR; + type HCRYPTKEY = LONG_PTR; + type ALG_ID = libc::c_uint; + + static PROV_RSA_AES: DWORD = 24; + static CRYPT_SILENT: DWORD = 64; + static CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000; + static CALG_SHA_256: ALG_ID = 0x800c; + static HP_HASHVAL: DWORD = 0x00000002; + + #[allow(non_snake_case)] + extern "system" { + fn CryptAcquireContextA(phProv: *mut HCRYPTPROV, + pszContainer: LPCSTR, + pszProvider: LPCSTR, + dwProvType: DWORD, + dwFlags: DWORD) -> BOOL; + fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> BOOL; + + + fn CryptCreateHash(hProv: HCRYPTPROV, Algid: ALG_ID, hKey: HCRYPTKEY, + dwFlag: DWORD, phHash: *mut HCRYPTHASH) -> BOOL; + fn CryptHashData(hHash: HCRYPTHASH, pbData: *mut BYTE, dwDataLen: DWORD, + dwFlags: DWORD) -> BOOL; + fn CryptGetHashParam(hHash: HCRYPTHASH, dwParam: DWORD, pbData: *mut BYTE, + pdwDataLen: *mut DWORD, dwFlags: DWORD) -> BOOL; + + fn CryptDestroyHash(hHash: HCRYPTHASH) -> BOOL; + } + + macro_rules! call( ($e:expr) => ({ + if $e == 0 { + fail!("failed {}: {}", stringify!($e), os::last_os_error()) + } + }) ) + + pub struct Sha256 { + hcryptprov: HCRYPTPROV, + hcrypthash: HCRYPTHASH, + } + + impl Sha256 { + pub fn new() -> Sha256 { + let mut hcp = 0; + call!(unsafe { + CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR, + PROV_RSA_AES, + CRYPT_VERIFYCONTEXT | CRYPT_SILENT) + }); + let mut ret = Sha256 { hcryptprov: hcp, hcrypthash: 0 }; + call!(unsafe { + CryptCreateHash(ret.hcryptprov, CALG_SHA_256, + 0, 0, &mut ret.hcrypthash) + }); + return ret; + } + + pub fn update(&mut self, bytes: &[u8]) { + call!(unsafe { + CryptHashData(self.hcrypthash, bytes.as_ptr() as *mut _, + bytes.len() as DWORD, 0) + }) + } + + pub fn final(&mut self) -> [u8, ..32] { + let mut ret = [0u8, ..32]; + let mut len = ret.len() as libc::DWORD; + call!(unsafe { + CryptGetHashParam(self.hcrypthash, HP_HASHVAL, ret.as_mut_ptr(), + &mut len, 0) + }); + assert_eq!(len as uint, ret.len()); + return ret; + } + } + + impl Drop for Sha256 { + fn drop(&mut self) { + if self.hcrypthash != 0 { + call!(unsafe { CryptDestroyHash(self.hcrypthash) }); + } + call!(unsafe { CryptReleaseContext(self.hcryptprov, 0) }); + } + } +} -- 2.30.2